package org.NooLab.texx;

import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;



import org.NooLab.structures.texx.plugg.algo.intf.AlgorithmIntf;
import org.NooLab.texx.app.TexxProcessControlIntf;
import org.NooLab.texx.components.AlgorithmsProvider;
import org.NooLab.texx.components.TexxApplicationEventIntf;
import org.NooLab.texx.components.TexxTasks;
import org.NooLab.texx.components.WorkDataObject;
import org.NooLab.texx.properties.TexxAppProperties;
import org.NooLab.texx.properties.TexxProperties;
import org.NooLab.texx.storage.TexxDataBase;
import org.NooLab.texx.tcp.TikaServerConnection;
 

import org.NooLab.tikaserver.intf.TikaSrvCmdPkgIntf;
import org.NooLab.tikaserver.tcp.agents.TikaSrvCmdPkg;
import org.NooLab.utilities.files.DFutils;
import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.net.GUID;
import org.NooLab.utilities.objects.StringedObjects;



/**
 * 
 * 
 * 
 * 
 *
 */
class Texx          
					implements 	Runnable,
								Observer,
								TexxIntf {


	TexxProperties texxProperties;
	TexxFactory texxFactory ;
	TexxProcessControlIntf texxProcessControl ;
	AlgorithmsProvider algorithmsProvider;
	private TexxPluginSettingsIntf pluginSettings;
	
	// ArrayList<TexxDataObject> somDataObjects = new ArrayList<TexxDataObject>();
	
	public TexxTasks texxTasks;
	TexxDataBase txxDataBase ;
	
	Texx txx ;
	TexxAppProperties txxAppProperties;
	
	
	boolean isActivated=false, isInitialized=false;
	boolean processIsRunning=false;
	Thread txxThread;
	
	private boolean processIsActivated=false;
	private boolean userBreak = false;

	SourceLoader srcLoader ;
	transient TexxApplicationEventIntf appInformer ;

	ArrayList<WorkDataObject> workDataObjects = new ArrayList<WorkDataObject>();
	
	String tikasrvTextData ="";
	int tikasrvReturn = 1;
	
	
	transient DFutils fileutil = new DFutils();
	transient StringedObjects sob = new StringedObjects();
	transient PrintLog out = new PrintLog(2, true, "[Texx]");
	
	// ========================================================================
	protected Texx( TexxFactory factory){
		
		out.setPrefix("[TEXX]") ;
		
		txxThread = new Thread (this,"txxThread" );
		texxTasks = new TexxTasks( texxFactory ) ;
		
		texxFactory = factory;
		texxProperties = texxFactory.getTxxProperties() ;
		
		texxProcessControl = texxFactory.getSomProcessControl() ;
		appInformer = texxFactory.appInformer ;
		
		txxDataBase = new TexxDataBase(texxProperties);
		txx = this;
	
		pluginSettings = texxProperties.getPluginSettings();
		
		
		algorithmsProvider = new AlgorithmsProvider (texxProperties);
		int z = algorithmsProvider.createAlgoObjects(AlgorithmIntf._ALGOTYPE_READER);
		if (z>0){
			pluginSettings.addAlgorithmsProvider(algorithmsProvider) ;
		}
		
		cleanTmpFolders();  
		
	}
	// ========================================================================
	
	public void performWork(TexxTask txxTask) throws Exception {
		// 
		MethodDispatcherDigester workResultHandler ;
		
		// This invoked class then will connect to plugins  
		// thus, if we have multiple documents, we should instantiate it only once
		MethodDispatcher medis;
		
		
		if (texxProperties.isPluginsAvailable()==false){
			txxTask.isCompleted = false;
			txxTask.isFailed = true;
			out.printErr(1, "No plugins available for handling input, \nthus the rquested task will not be performed.");
			return;
		}
		
		txxTask.setDescription("MethodDispatcher()") ;
		txxTask.setWorkersHost(null) ;
		
		medis = new MethodDispatcher( this , txxTask, texxFactory );

		// optional: LOOP START here across all sources
		// TODO  there, we create a data object for any of the sources in the current bunch		
		// with loop: texxProperties.getDataSrcFilename();
		
		workDataObjects.clear();
		if (txxTask.getSourceType()<=TexxTaskIntf._TEXXTASK_SOURCE_TYPE_FILE){
				
			performOnSingleSource(medis,txxTask);
		}else{
			// in batch mode, medis will load the sources itself...
			// this will contain a thread with waiter's loop and retrieval stuff 
			(new MethodDispatcherServer(medis, txxTask)).go();
		}
		
				
	}

	private void performOnSingleSource(MethodDispatcher medis, TexxTask txxTask) throws Exception {
		MethodDispatcherDigester workResultHandler ;
		
		
		workResultHandler = new MethodDispatcherDigester( medis , texxFactory);
		
		// in batch mode, medis will load the sources itself...
		Object obj ;
	 	 
		obj = getSrcLoader().load( texxProperties.getDataSrcFilename() );
		
		WorkDataObject _workDataObject = (WorkDataObject)obj ;
		
		long adhocDocId = Math.abs(10 + 2147483647+_workDataObject.getSourceUrl().hashCode()) ;
		PrintLog.Delay(2) ;
		_workDataObject.setSourceId( System.currentTimeMillis()  ) ;
		
		medis.setWorkDataObject( _workDataObject );
		
		 
		// wait for release of task, then reset it and prepare it for next source 
		txxTask.setWorkResultHandler( workResultHandler  ) ;

		// medis.checkConsistencyOfRequest() ;
		
		medis.perform( (WorkDataObject)obj ); // carte blanche... will dispatch according to the task
		// will return in "onTaskCompleted()"
		// there, we will collect the output data into a structured file 
		//  == serialized object, !! not the final table !!
		// the final table will be created after the "last" source of
		// the current bunch has been taken
		// sometimes, we will start with a small set of documents, then peoceed as service...
		
	}

	
	
	@Override
	public void onTaskCompleted( TexxTask txxTask ) {
		 
		out.printErr(1, "\nSomFluid task has been completed, returning instance : "+ txxTask.getDescription()+", "+txxTask.getSomHost().toString().replace("org.NooLab.somfluid.", "")+"\n" );
		
		/* txxTask contains a reference to 
		 *   - somHost
		 *   - the SomResultDigesterIntf
		 * thus we just can call the interface's method ...
		 * the routing to the appropriate class will be taken "automatically" (we prepared it in the task specific method)
		 * 
		 * it will use persistence settings
		 */
		
		
		
		// 
		
		if (txxTask.isCompleted){
			
			// not active yet... SomResultDigesterIntf resultHandler = txxTask.getSomResultHandler();
			// not active yet... resultHandler.handlingResults() ;
			
			if (TexxTask.taskIsModelOptimizer(txxTask)){ 

				TexxTask _task = texxFactory.texxModule.texxTasks.getItemByGuid(txxTask.guidID);
				if (_task == null) {
					texxTasks.add(txxTask);
				}

				// put the model to the list
				// provide an option ...
				texxFactory.getWorkerObjects().addTexx( txxTask.txxHost, txxTask.guidID);

				// ModelOptimizer moz = (ModelOptimizer) txxTask.somHost;
				// moz.saveResults();

				out.printErr(2, "\nThe following task has been finished and closed: " + txxTask.guidID + "\n");
				
			}
		} // completed "ModelOptimizer"
		
		if (TexxTask.taskIsSomApplication(txxTask)){ 
			
			out.printErr(2,"\nThe following task has been finished and closed: "+txxTask.guidID+"\n");
			
		}  // completed "SomApplication"
	}




	// ========================================================================
	public String addTask(TexxTask workTask) {
		String mstr;
		
		workTask.guidID = GUID.randomvalue() ;
		
		texxTasks.add(workTask);
		
		texxTasks.setSfFactory(texxFactory) ;
		 
		 
		if (texxTasks.size()==1){
			mstr = "is 1 task";
		}else{
			mstr = "are "+texxTasks.size()+" tasks";
		}
		out.print(2, "...now there "+mstr+" in the queue...") ; 
		
		isActivated = true;
		
		return workTask.guidID  ;
	}




	@Override
	public void start() throws Exception {
		boolean dbOk;
		
		dbOk = txxDataBase.prepareDatabase("randomwords");
		
		if (processIsActivated){
			return;
		}
		processIsActivated=true;
		if(processIsRunning==false){
			txxThread.start();
			isActivated = true;
		}
	}

	
	@Override
	public void run() {
		
		int z=0;
		boolean isWorking = false;
		TexxTask txxTask = null;
		
		processIsRunning=true;
		
		out.print(2,"task dispatching process is being started...");
		
		try{
			
			while (processIsRunning){
				
				//out.print(2,"task dispatching process is running (tasks:"+ texxTasks.size()+") ");
				
				if ((isWorking==false) && (isActivated)){//) && (isInitialized)){
					isWorking=true;
											out.print(5,"...tdp (2)") ;
					if ((texxTasks!=null) && (texxTasks.size()>0)){
										    out.print(5,"...tdp (3)") ;
			            z++;
					    // take the first in queue
						txxTask = texxTasks.getItem(0) ;
						// actually, we need a selection loop??? to get the FIRST non-treated one?
						
						if ((txxTask!=null) && (txxTask.taskDispatched==0) && (txxTask.isCompleted==false) && (txxTask.isExported==false)){
							out.print(2,"\nTexx working on task, id = "+txxTask.guidID);
							
							// out.print(2,"expected infimum number of explored variable combinations : " +n+"\n");
									 
							txxTask.taskDispatched=1;
							TaskDispatcher td = new TaskDispatcher(txxTask);
							 
							texxTasks.remove(0) ;
							
							if (td.isWorking==false){
								out.print(5,"...tdp (91)") ;
								isWorking=false;
							}

							if (z%10==0){
								System.gc() ;
							}
						}else{
							isWorking=false;	
						}
											out.print(5,"...tdp (4)") ;
					}else{
						isWorking=false;
					}
					
					
				}
				
				if ((isWorking) && (texxTasks.size()>0) && (texxTasks.getItem(0).isCompleted())){
											out.print(5,"...tdp (6)") ;
					out.print(2,"task ("+texxTasks.getItem(0).guidID+") has been completed.\n"); // yet, the completion flag is set by the process itself !
					texxTasks.setStopped(true) ; // remove(0);
					isWorking=false;
				}
				out.delay(500);
			}
			
											out.print(5,"...tdp (-1)") ;
		}catch(Exception e){
			e.printStackTrace();
		}
	}

	// ..........................................
	class TaskDispatcher{
	
		private boolean isWorking = false;
	
		
		public TaskDispatcher(TexxTask txxTask) {
			 
			int r;
			
			try{

				String ttyp = txxTask.getTaskType() ;
				String _typeId = ttyp.toLowerCase();

				
				texxTasks.add(txxTask) ;
				
				
				// dependent on task we invoke different methods and worker classes
				
				if ( TexxTask.taskIsDefault( _typeId ) ){ // replace by a proc + constant : modeling
					
					if (txxTask.workType == TexxProperties._WORKER_TYPE_DEFAULT){
						/*
						// accessing the persistent file,
						// it may be an external file containing raw data, or
						// if sth exists an already prepared one
						
						
						if (txxTask.workingMode == TexxTask._SOM_WORKMODE_PIKETT){ 
							// goto standby mode for this task
							// files will be loaded as soon as they appear on the radar...
							txxTask.isStandbyActive = true;
						}
						*/
						
						if (txxTask.workingMode == TexxTask._SOM_WORKMODE_FILE){ // ==default
							// r = texxFactory.loadSource(); // there does not happen anything
							
						}
						
						performWork( txxTask );
						
						this.isWorking=true;
						
	
					}else{
					
					}
				}
				// if ( TexxTask.taskIs <other >( _typeId )  ){  }
					  
				
			}catch(Exception e){
				// if userbreak == false
				e.printStackTrace();
				this.isWorking=false;
			}
			
		}
		
		
	} // inner class TaskDispatcher


	
	@Override
	public void setUserbreak(boolean flag) {

		userBreak = flag;
	}

	// ========================================================================
	
	@Override
	public boolean getUserbreak() {
		
		return userBreak ;
	}




	private void cleanTmpFolders() {
		String tmpdir = fileutil.getTempDir(); // fileutil.createTempDir(""); // createTempDir("");
		// SomDataObject._TEMPDIR_PREFIX)
		DFutils.reduceFolderListByAge( tmpdir,20, "~noo-txx", 2.3) ; // 2.3 days 
	}

	
	public WorkDataObject createWorkDataObject() {
		
		WorkDataObject _wrkDataObject;
		
		
		_wrkDataObject = new WorkDataObject( texxProperties ) ;
		
		_wrkDataObject.setFactory( texxFactory );
		_wrkDataObject.setOut(out);
		 
		_wrkDataObject.prepare();
		
		workDataObjects.add(_wrkDataObject) ;
		
		_wrkDataObject.setIndex(workDataObjects.size()-1);
		
		return _wrkDataObject;
	}

	
	class SourceLoader{
		

		public Object load(String srcname) throws Exception {


			WorkDataObject workDataObject;
			int result=-1;
			String srcName ="";
			String loadedsrc ;
			
			
			//  
			srcName = srcname;
			if ((srcName.length()==0) ){ // || (DFutils.fileExists(srcName)==false)
				srcName = texxProperties.getDataSrcFilename();
			}
			
			if (DFutils.fileExists(srcName)==false){
				String estr= "File not found in loadSource(): "+srcName;
				if (srcName.length()==0){
					estr = "Application ids in <single document mode>, but no filename has been provided to work on.";
				}
				throw(new Exception(estr));
			}
			// check whether there is already an SDO
			/*
			if (somDataObjects==null){
				somDataObjects = new ArrayList<SomDataObject>();
			}
			for (int i=0;i<somDataObjects.size();i++){
				somDataObject = somDataObjects.get(i) ;
				loadedsrc = somDataObject.getDataReceptor().getLoadedFileName() ;
				if (loadedsrc.contentEquals(srcname)){
					// return _somDataObject;
				}
				somDataObjects.get(0).clear();
			}
			
			*/
			
			workDataObject = createWorkDataObject() ;
		
			
			// SomTransformer transformer = new SomTransformer( somDataObject, sfProperties );
		
			DataReceptor dataReceptor = new DataReceptor( workDataObject );
			
			// establishes a "DataTable" from a physical source
			dataReceptor.loadFromFile(srcName);
		
			 
			// imports the DataTable into the SomDataObject, and uses a SomTransformer instance 
			// in order to provide a basic numeric version of the data by calling SomTransformer.basicTransformToNumericalFormat()
			// read the plain text using dedicated plugins...
			
			String ctxt = texxProperties.getApplicationContext();
			
			if (ctxt.contentEquals(TexxProperties._APP_CONTEXT_ITEXX)==false){
				out.printErr(3, "loading doc via plugin...");
				workDataObject.importData( dataReceptor, 1 );
			}else{
				out.printErr(3, "loading doc via x-server...");
				workDataObject.importDataByTikaServer( dataReceptor, 1 ); // via serialized objects and tcp....
			}
			// workDataObject.importDataByPlugin(dataReceptor, 1);
			
			// this data object now contains the text, a unique id, the url and a time stamp
			// such we could put it into a data base, or create a meta data reference list
	 
			workDataObject.fileReceiptGuid = workDataObject.save(0);
			
			return workDataObject;

		}
	}



	public SourceLoader getSrcLoader() {
		return new SourceLoader();
	}

	
	
	
	@SuppressWarnings("unused")
	private void testingTikaServerConnection() {
		
		int r=-1;
		String str;
		
		
		
		TikaServerConnection tsc = new TikaServerConnection(this, texxFactory, true);
		
		tsc.setWaitingMode( TikaServerConnection._TCP_CLIENT_WAITING_MODE_COMPLETION );
		
		tikasrvReturn = -99 ;
		try {
			
			str = tsc.send("D:/kwa/writing/doc/biodiv.doc");
			
		} catch (Exception e) {
			
			return;
		}
		
		out.print(2, "waiting loop after send, before receiving results (b)...");
		while (tikasrvReturn == -99){
			out.delay(10);
		}
		String text = tikasrvTextData;
		out.print(2, "...returned from TikaConnection, data (len:"+text.length()+") is : \n"+text);
		
		out.delay(5000);
		tsc.stop();
	}
	
	
	@Override
	public void update(Observable sender, Object objText) {
		String text = "";
		
		
		if (objText instanceof String){
			text = (String)objText;
		}
		
		tikasrvTextData = text ;
		tikasrvReturn = 1;
	}
	

	// ========================================================================

	/*
	 public void getPlainText() {
		// TODO Auto-generated method stub
		
		// the actual readers are in the plugin files 
		if (rawFileData.binaryFormat){
			// get the format through format checker
			// must be html, POI (=pdf,word,xls etc ), text, pages? ->

			// get may deliver a table, a stream, a string etc.
			// dataTable = rawFileData.get(); 
			

		}else{
			// determine textual format: 
			// text, html, xml, other tags, table (csv, tab sep, semi colon)  
		}
		
	}

	 */
	
	 

}
